package com.lan.workflow.controller;



import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.lan.workflow.annotation.Log;
import com.lan.workflow.common.ResponseCodeEnum;
import com.lan.workflow.util.WorkflowUtil;
import com.lan.workflow.vo.ActReModelVO;
import com.lan.workflow.service.IActReModelService;
import org.activiti.bpmn.BpmnAutoLayout;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.editor.language.json.converter.util.CollectionUtils;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.explorer.util.XmlUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author lanwp
 * @since 2020-07-31
 */
@RestController
@RequestMapping("/model")
public class ActReModelController {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private IActReModelService actReModelService;

    private BpmnXMLConverter bpmnXmlConverter = new BpmnXMLConverter();
    private BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();

    @Log
    @PostMapping(value = "/page", produces = {"application/json;charset=utf-8;"})
    public Map<String,Object> page(@RequestParam Integer size, Integer current, @RequestBody Map<String,Object>  requestMap) throws Exception {
        Map<String, Object> result = new HashMap<>();

        IPage<ActReModelVO> page = actReModelService.page(size,current, requestMap);
        List<ActReModelVO> records = page.getRecords();
        for(ActReModelVO model : records){
            String metaInfo = model.getMetaInfo();
            if(!StringUtils.isEmpty(metaInfo)){
                Map<String, Object> metaInfoMap = objectMapper.readValue(metaInfo, new TypeReference<Map<String, Object>>() {});
                String description = metaInfoMap.get("description") == null ? null : metaInfoMap.get("description").toString();
                if(!StringUtils.isEmpty(description)){
                    model.setDescription(description);
                }
            }
        }

        result.put("code", ResponseCodeEnum.SUCCESS.getKey());
        result.put("data", page);
        return result;
    }

    @Log
    @PostMapping(value = "/saveOrUpdate", produces = {"application/json;charset=utf-8;"})
    public Map<String,Object> saveOrUpdateModel(@RequestBody Map<String,Object> requestMap) throws Exception {
        Map<String, Object> result = new HashMap<>();
        String id = requestMap.get("id") == null ? null : requestMap.get("id").toString();
        String key = requestMap.get("key") == null ? null : requestMap.get("key").toString();
        String name = requestMap.get("name") == null ? null : requestMap.get("name").toString();
        //String category = requestMap.get("category") == null ? null : requestMap.get("category").toString();
        String description = requestMap.get("description") == null ? null : requestMap.get("description").toString();
        Integer revision = (Integer) requestMap.getOrDefault("revision", 1);
        if(StringUtils.isEmpty(key) || StringUtils.isEmpty(name)){
            result.put("code", ResponseCodeEnum.BODY_NOT_MATCH.getKey());
            result.put("msg", "业务表、模型名必须填！");
        } else {
            if(StringUtils.isEmpty(id)){
                //初始化一个空模型
                Model model = repositoryService.newModel();

                ObjectNode modelNode = objectMapper.createObjectNode();
                modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
                modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
                modelNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);

                model.setKey(key);
                model.setName(name);
                model.setCategory(key);
                model.setMetaInfo(modelNode.toString());

                repositoryService.saveModel(model);
                id = model.getId();

                ObjectNode editorNode = objectMapper.createObjectNode();
                editorNode.put("id", "canvas");
                editorNode.put("resourceId", "canvas");
                ObjectNode stencilSetNode = objectMapper.createObjectNode();
                stencilSetNode.put("namespace","http://b3mn.org/stencilset/bpmn2.0#");
                editorNode.put("stencilset" , stencilSetNode);
                ObjectNode propertiesNode = objectMapper.createObjectNode();
                propertiesNode.put("process_id", model.getKey());
                propertiesNode.put("name", model.getName());
                propertiesNode.put("process_namespace", model.getKey());
                editorNode.put("properties", propertiesNode);

                repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));
            } else {
                Model model = repositoryService.getModel(id);

                ObjectNode modelNode = objectMapper.createObjectNode();
                modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
                modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
                modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);

                model.setKey(key);
                model.setName(name);
                model.setCategory(key);
                model.setMetaInfo(modelNode.toString());
                repositoryService.saveModel(model);

                ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(
                        new String(repositoryService.getModelEditorSource(model.getId()), "utf-8"));
                ObjectNode propertiesNode = (ObjectNode) editorJsonNode.get("properties");
                propertiesNode.put("process_id", model.getKey());
                propertiesNode.put("name", model.getName());
                propertiesNode.put("process_namespace", model.getKey());
                editorJsonNode.put("properties", propertiesNode);
                repositoryService.addModelEditorSource(id, editorJsonNode.toString().getBytes("utf-8"));
            }
            result.put("code", ResponseCodeEnum.SUCCESS.getKey());
            result.put("msg", ResponseCodeEnum.SUCCESS.getValue());
        }
        return result;
    }

    @Log
    @RequestMapping(value = "/importProcessModel", method = RequestMethod.POST, produces = "application/json")
    public ObjectNode importProcessModel(@RequestParam("file") MultipartFile file) {
        ObjectNode result = objectMapper.createObjectNode();
        String fileName = file.getOriginalFilename();

        if (fileName != null && (fileName.endsWith(".bpmn") || fileName.endsWith(".bpmn20.xml"))) {
            try {
                XMLInputFactory xif = XmlUtil.createSafeXmlInputFactory();
                InputStreamReader xmlIn = new InputStreamReader(file.getInputStream(), "UTF-8");
                XMLStreamReader xtr = xif.createXMLStreamReader(xmlIn);
                BpmnModel bpmnModel = bpmnXmlConverter.convertToBpmnModel(xtr);
                if (CollectionUtils.isEmpty(bpmnModel.getProcesses())) {
                    throw new RuntimeException("No process found in definition " + fileName);
                }

                if (bpmnModel.getLocationMap().size() == 0) {
                    BpmnAutoLayout bpmnLayout = new BpmnAutoLayout(bpmnModel);
                    bpmnLayout.execute();
                }

                ObjectNode bpmnModelNode = bpmnJsonConverter.convertToJson(bpmnModel);

                org.activiti.bpmn.model.Process process = bpmnModel.getMainProcess();
                String name = process.getId();
                if (org.apache.commons.lang3.StringUtils.isNotEmpty(process.getName())) {
                    name = process.getName();
                }
                String description = process.getDocumentation();

                Model model = repositoryService.newModel();

                ObjectNode modelNode = objectMapper.createObjectNode();
                modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
                modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
                modelNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);

                model.setKey(process.getId());
                model.setName(name);
                model.setCategory(model.getKey());
                model.setMetaInfo(modelNode.toString());

                repositoryService.saveModel(model);

                repositoryService.addModelEditorSource(model.getId(), bpmnModelNode.toString().getBytes("utf-8"));

                result.put("code", "200");
                result.put("msg", "sucess");
                result.putPOJO("data", model);
            } catch (Exception e) {
                result.put("code", "500");
                result.put("msg", "Import failed for " + fileName);
                throw new RuntimeException("Import failed for " + fileName + ", error message " + e.getMessage());
            }
        } else {
            result.put("code", "500");
            result.put("msg", "Invalid file name, only .bpmn and .bpmn20.xml files are supported not " + fileName);
        }

        return result;
    }

    /*@Log
    @GetMapping(value = {"/delete/{modelId}"}, produces = {"application/json"})
    public Map<String,Object> delete(@PathVariable String modelId) throws Exception {
        Map<String,Object> result = new HashMap();

        repositoryService.deleteModel(modelId);
        result.put("code", ResponseCodeEnum.SUCCESS.getKey());
        result.put("msg", ResponseCodeEnum.SUCCESS.getValue());
        return result;
    }*/

    @Log
    @GetMapping(value = {"/bpmn/{modelId}"}, produces = {"application/json"})
    public void getBpmn(@PathVariable String modelId, HttpServletResponse response) throws Exception {
        response.setContentType("text/plain;charset=UTF-8");
        String bpmn = WorkflowUtil.getBpmn(modelId, this.repositoryService);
        response.getWriter().print(bpmn);
    }

    @Log
    @GetMapping(value = {"/exportXml/{modelId}"})
    public void exportXml(@PathVariable String modelId, HttpServletResponse response) throws Exception {
        Model modelData = repositoryService.getModel(modelId);
        ObjectNode modelNode = (ObjectNode) objectMapper.readTree(repositoryService.getModelEditorSource(modelData.getId()));
        BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
        String processName = modelData.getName() + ".bpmn20.xml";

        response.setContentType("text/xml;charset=gbk");
        response.setHeader("Content-Disposition", "attachment; filename="
                + new String(processName.getBytes("gbk"),"iso-8859-1"));
        response.getOutputStream().write(bpmnBytes);
    }

    @Log
    @GetMapping(value = "deploy/{modelId}")
    public ObjectNode deploy(@PathVariable("modelId") String modelId) throws Exception {
        ObjectNode result = objectMapper.createObjectNode();
        Model model = repositoryService.getModel(modelId);
        ObjectNode modelNode = (ObjectNode) objectMapper.readTree(repositoryService.getModelEditorSource(model.getId()));

        BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(modelNode);
        WorkflowUtil.checkModel(bpmnModel);

        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
        String processName = model.getName() + ".bpmn20.xml";
        /*List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionResourceName(processName).list();
        for (ProcessDefinition definition:list){
            //删除原有流程定义,正在使用的流程定义无法删除
            repositoryService.deleteDeployment(definition.getDeploymentId());
        }*/
        Deployment deployment = repositoryService.createDeployment().name(model.getName())
                .category(model.getCategory()).addString(processName, new String(bpmnBytes)).deploy();

        model.setDeploymentId(deployment.getId());
        repositoryService.saveModel(model);

        result.put("code", "200");
        result.put("msg", "sucess");
        return result;
    }
}

